package com.easylinkin.linkappapi.positioning.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.easylinkin.linkappapi.common.utils.CommonUtils;
import com.easylinkin.linkappapi.common.utils.DateUtil;
import com.easylinkin.linkappapi.common.utils.StringUtil;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.deviceattributestatus.entity.DeviceAttributeStatus;
import com.easylinkin.linkappapi.elasticsearch.entity.SortField;
import com.easylinkin.linkappapi.elasticsearch.entity.TimeRange;
import com.easylinkin.linkappapi.elasticsearch.service.IEService;
import com.easylinkin.linkappapi.gaodemap.entity.ConvertLocationParams;
import com.easylinkin.linkappapi.gaodemap.entity.InverseCodingParams;
import com.easylinkin.linkappapi.gaodemap.service.GaoDeApiService;
import com.easylinkin.linkappapi.positioning.entity.CarDeviceEs;
import com.easylinkin.linkappapi.positioning.entity.DevicePositionHistory;
import com.easylinkin.linkappapi.positioning.entity.DeviceRealTimePosition;
import com.easylinkin.linkappapi.positioning.entity.QueryRealTimePosition;
import com.easylinkin.linkappapi.positioning.service.RealTimePositionService;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;


/**
 * @program: linkapp-group
 * @description: 实时跟踪
 * @author: chenkaixuan
 * @create: 2021-06-24 12:25
 */
@Slf4j
@Service
public class RealTimePositionServiceImpl implements RealTimePositionService {
    private static String LONGITUDE="longitude";
    private static String LATITUDE="latitude";
    @Resource
    DeviceService deviceService;
    @Resource
    IEService realService;
    @Resource
    LinkappTenantService linkappTenantService;
    @Resource
    GaoDeApiService gaoDeApiService;


    /**
     * 查询实时追踪
     * @return
     */
    @Override
    public DeviceRealTimePosition getDeviceRealTimeData(QueryRealTimePosition queryRealTimePosition) throws IOException, ExecutionException, InterruptedException {
        //1.根据设备编号，查询设备数据
        Assert.notNull(queryRealTimePosition,"参数不能为空!");
        Assert.notNull(queryRealTimePosition.getDeviceCode(),"deviceCode 不能为空!");
        DeviceRealTimePosition deviceRealTimePosition=null;
        Device device=null;
        //2.根据设备编号查询一条es最新上报数据
        if(queryRealTimePosition.getReportTime() != null){
            //2.1 如果上报时间reportTime不为空，则 查询上报时间最后的所有数据
            device=deviceService.findOneByDeviceCode(queryRealTimePosition.getDeviceCode());
            if(device == null){
                return null;
            }
            deviceRealTimePosition=getEsDateHandler(queryRealTimePosition);
        }
        if(queryRealTimePosition.getReportTime() == null || deviceRealTimePosition == null ||
                deviceRealTimePosition.getDevicePositionHistoryList() == null || deviceRealTimePosition.getDevicePositionHistoryList().size()<=0){
            //2.2  reportTime,或者查询es无数据 为空，则查询最新一条数据返回
            IPage<Device> monitorPage = deviceService.getMonitorPage(new Page(0, 1), new Device().setCode(queryRealTimePosition.getDeviceCode()));
            List<Device> deviceList=monitorPage.getRecords();
            if(deviceList == null || deviceList.size()<=0){
                return null;
            }
            device=deviceList.get(0);
            deviceRealTimePosition=handlerResultDeviceDate(device);
        }
        //复制device属性到deviceRealTimePosition
        copyObjectProperties(device,deviceRealTimePosition);
        //坐标转换
        convertLocation(deviceRealTimePosition);
        //3.通过es最新数据经纬度，查询api获取地址
        getAddress(deviceRealTimePosition);
        return deviceRealTimePosition;
    }

    private void convertLocation(DeviceRealTimePosition deviceRealTimePosition) throws ExecutionException, InterruptedException {
        if(deviceRealTimePosition == null){
            return;
        }

        if(deviceRealTimePosition.getDevicePositionHistoryList() == null || deviceRealTimePosition.getDevicePositionHistoryList().size()<=0){
            ConvertLocationParams convertLocationParamsv=new ConvertLocationParams();
            String address = deviceRealTimePosition.getLongitude() + "," + deviceRealTimePosition.getLatitude();
            convertLocationParamsv.setLocation(address);
            List<ConvertLocationParams> collect=Arrays.asList(convertLocationParamsv);
            //转换坐标
            Map<String, String> stringStringMap = gaoDeApiService.convertLocation(collect);
            if(stringStringMap == null ){
                return;
            }
            address = stringStringMap.get(address);
            if (StringUtils.isEmpty(address) || address.indexOf(",") == -1) {
                return;
            }
            String[] split = address.split(",");
            if (StringUtils.isEmpty(split[0]) || StringUtils.isEmpty(split[1])) {
                return;
            }
            deviceRealTimePosition.setLongitudeConvert(Double.parseDouble(split[0]));
            deviceRealTimePosition.setLatitudeConvert(Double.parseDouble(split[1]));
            return;
        }
        //组装坐标转换参数
        List<ConvertLocationParams> collect = deviceRealTimePosition.getDevicePositionHistoryList().stream().map(m -> {
            ConvertLocationParams convertLocationParams = new ConvertLocationParams();
            convertLocationParams.setLocation(m.getLongitude() + "," + m.getLatitude());
            return convertLocationParams;
        }).collect(Collectors.toList());
        //转换坐标
        Map<String, String> stringStringMap = gaoDeApiService.convertLocation(collect);
        if(stringStringMap == null || stringStringMap.isEmpty()){
            return;
        }
        //获取坐标到集合
        List<DevicePositionHistory> devicePositionHistories = deviceRealTimePosition.getDevicePositionHistoryList().stream().map(m -> {
            String address = m.getLongitude() + "," + m.getLatitude();
            address = stringStringMap.get(address);
            if (StringUtils.isEmpty(address) || address.indexOf(",") == -1) {
                return m;
            }
            String[] split = address.split(",");
            if (StringUtils.isEmpty(split[0]) || StringUtils.isEmpty(split[1])) {
                return m;
            }
            m.setLongitudeConvert(Double.parseDouble(split[0]));
            m.setLatitudeConvert(Double.parseDouble(split[1]));
            return m;

        }).collect(Collectors.toList());
        if(devicePositionHistories != null && devicePositionHistories.size()>0){
            deviceRealTimePosition.setLongitudeConvert(devicePositionHistories.get(0).getLongitudeConvert());
            deviceRealTimePosition.setLatitudeConvert(devicePositionHistories.get(0).getLatitudeConvert());
        }
        deviceRealTimePosition.setDevicePositionHistoryList(devicePositionHistories);
    }

    /***
     *  获取地址
     * @return
     */
    private  void getAddress(DeviceRealTimePosition deviceRealTimePosition){
        if(StringUtils.isEmpty(deviceRealTimePosition)){
            return;
        }
        try {
            String address=deviceRealTimePosition.getLongitudeConvert() + "," + deviceRealTimePosition.getLatitudeConvert();
            InverseCodingParams inverseCodingParams = new InverseCodingParams();
            inverseCodingParams.setLocation(address);
            Map<String, String> geographyInverseCoding = gaoDeApiService.getGeographyInverseCoding(Arrays.asList(inverseCodingParams));
            if(geographyInverseCoding != null){
                address=geographyInverseCoding.get(address);
                deviceRealTimePosition.setAddress(address);
            }
        } catch (ExecutionException | InterruptedException e) {
            log.error("实时跟踪请求高德逆地址错误msg:{}",e.getMessage());
            throw new IllegalArgumentException(e.getMessage());
        }
    }

    /***
     * 反射处理，set设备属性值到对象，返回统一
     * @param device
     * @return
     */
    private DeviceRealTimePosition handlerResultDeviceDate(Device device){
        List<DeviceAttributeStatus> deviceAttributeStatusList = device.getDeviceAttributeStatusList();
        if(deviceAttributeStatusList == null || deviceAttributeStatusList.size()<=0){
            return null;
        }
        Map<String, String> collect = deviceAttributeStatusList.stream().collect(Collectors.toMap(m -> m.getPropCode(), m -> m.getPropValue(), (key1, key2) -> key2));

        DeviceRealTimePosition deviceRealTimePosition=new DeviceRealTimePosition();
        Field[] declaredFields = deviceRealTimePosition.getClass().getDeclaredFields();
        try {
            for (Field declaredField : declaredFields) {
                String propValue=collect.get(declaredField.getName());
                if(!StringUtils.isEmpty(propValue)){
                    PropertyDescriptor pd = new PropertyDescriptor(declaredField.getName(), deviceRealTimePosition.getClass());
                    //获取set方法
                    Method method = pd.getWriteMethod();
                    //动态参数类型装换
                    Object value=paramTypeConvert(declaredField,propValue);
                    //赋值
                    method.invoke(deviceRealTimePosition,value);
                }
            }
        } catch (IllegalAccessException | InvocationTargetException | IntrospectionException | NoSuchMethodException | ClassNotFoundException e) {
            log.error("查询实时跟踪反射set数据错误:{}",e.getMessage());
            throw new IllegalArgumentException(e.getMessage());
        }
        if(StringUtils.isEmpty(deviceRealTimePosition)){
            return null;
        }
        return deviceRealTimePosition;
    }

    /***
     * 参数类型动态转换
     * @param field
     * @param value
     * @return
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    private  Object paramTypeConvert(Field field,String value) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //field为反射出来的字段类型
        String ftype = field.getType().getName();
        String fstype = field.getType().getSimpleName();
        if (field.getType() == String.class)
        {
            return value;
        }
        else if (ftype.indexOf("java.lang.") == 0) {
            // java.lang下面类型通用转换函数
            Class<?> class1 = Class.forName(ftype);
            Method method = class1.getMethod("parse" + parseParam(fstype),String.class);
            if (method != null) {
                Object ret = method.invoke(null, value);
                if (ret != null)
                {
                    return ret;
                }
            }
        }
        return value;
    }

    private static Object parseParam(String fstype){
        switch (fstype)
        {
            case "Integer":
                return "Int";
        }
        return fstype;
    }

    /***
     * 复制对象属性
     * @param device
     * @param deviceRealTimePosition
     */
    private void copyObjectProperties(Device device,DeviceRealTimePosition deviceRealTimePosition){
        if(StringUtils.isEmpty(device) || StringUtils.isEmpty(deviceRealTimePosition)){
            return;
        }
        deviceRealTimePosition.setDeviceCode(device.getCode());
        deviceRealTimePosition.setDeviceName(device.getName());
        deviceRealTimePosition.setOnlineState(device.getOnlineState());
        deviceRealTimePosition.setStatus(device.getStatus());
        deviceRealTimePosition.setLastPushTime(device.getLastPushTime());
    }

    /***
     * 对es数据对象解析
     * @return
     */
    private DeviceRealTimePosition getEsDateHandler(QueryRealTimePosition queryRealTimePosition) throws IOException {
        Map<String, Object> likeQueryMap = new HashMap<>(16);
        likeQueryMap.put("deviceCodeTerm",queryRealTimePosition.getDeviceCode());

        TimeRange timeRange = new TimeRange();
        timeRange.setEndTime(DateUtil.getYYYYMMDDHHMMSSDate(new Date()))
                .setStartTime(DateUtil.getYYYYMMDDHHMMSSDate(queryRealTimePosition.getReportTime()))
//                .setFieldName("data.positioning_time.keyword");
                .setFieldName("data.positioning_time");

        SearchResponse searchResponse = queryEsData(likeQueryMap, 1, 10000, timeRange);
        //游标查询返回ID
        String scrollId = null;
        int j=0;
        List<DevicePositionHistory> devicePositionHistoryList = new ArrayList<DevicePositionHistory>();
        DeviceRealTimePosition deviceRealTimePosition = new DeviceRealTimePosition();
        scrollId=getEsDateAnalysis(devicePositionHistoryList,j,deviceRealTimePosition,searchResponse);
        while(true){
            if(scrollId == null){
                break;
            }
            searchResponse = realService.getScrollData(scrollId);
            scrollId=getEsDateAnalysis(devicePositionHistoryList,j,deviceRealTimePosition,searchResponse);
        }
        //如果最后值跳过，则手动set
        if(StringUtils.isEmpty(deviceRealTimePosition)){
            deviceRealTimePosition=new DeviceRealTimePosition();
            if(devicePositionHistoryList != null && devicePositionHistoryList.size()>0){
                DevicePositionHistory devicePositionHistory = devicePositionHistoryList.get(j);
                deviceRealTimePosition.setLatitude(devicePositionHistory.getLatitude());
                deviceRealTimePosition.setLongitude(devicePositionHistory.getLongitude());
            }
        }
        log.debug("查询结果长度："+devicePositionHistoryList.size());
        deviceRealTimePosition.setDevicePositionHistoryList(devicePositionHistoryList);
        return  deviceRealTimePosition;
    }

    /***
     * 解析es数据
     * @param devicePositionHistoryList
     * @param j
     * @param deviceRealTimePosition
     * @param searchResponse
     */
    private  String getEsDateAnalysis(List<DevicePositionHistory> devicePositionHistoryList,int j,DeviceRealTimePosition deviceRealTimePosition,SearchResponse searchResponse){
        String scrollId=null;
        if (Objects.nonNull(searchResponse) && Objects.equals(searchResponse.status(), RestStatus.OK)) {
            SearchHit[] hits = searchResponse.getHits().getHits();
            if(hits != null && hits.length>0){
                long length = searchResponse.getHits().totalHits;
                log.debug("scrollHits length=" + hits.length);
                int i=0;
                for (SearchHit searchHit : hits) {
                    i++;
                    String sourceAsString = searchHit.getSourceAsString();
                    if(sourceAsString == null){
                        continue;
                    }
                    JSONObject jsonObject = JSONObject.parseObject(sourceAsString);
                    if(jsonObject == null || jsonObject.size()<=0){
                        continue;
                    }
                    String data=jsonObject.getString("data");
                    //没有经纬度数据，则跳过
                    if(data == null || data.indexOf(LATITUDE) == -1 || data.indexOf(LONGITUDE) == -1){
                        continue;
                    }
                    DevicePositionHistory dr = JSON.parseObject(data,DevicePositionHistory.class);
                    devicePositionHistoryList.add(dr);
                    //最后一个add值下标
                    j++;
                    if(i >= length){
                        //取出最后一条
                        DeviceRealTimePosition deviceRealTimePositionJson = JSON.parseObject(data, DeviceRealTimePosition.class);
                        BeanUtils.copyProperties(deviceRealTimePositionJson,deviceRealTimePosition);
                    }
                }
                scrollId=searchResponse.getScrollId();
            }
        }
        return scrollId;
    }

    /***
     * 查询es
     * @param likeQueryMap
     * @param index
     * @param size
     * @param timeRange
     * @return
     */
    private SearchResponse queryEsData(Map<String, Object> likeQueryMap, int index, int size, TimeRange timeRange){
        SortField sortField = new SortField().setFieldName("storageTime").setSortOrder(SortOrder.ASC);
        List<String> necessaryFieldList = new ArrayList<>();
        necessaryFieldList.add("positioning_time");
        SearchResponse searchResponse = realService.getDeviceFlowListFromES(likeQueryMap, index, size, timeRange, necessaryFieldList,
                sortField, null);
        return searchResponse;
    }
}
